﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace PascalTriangle
{
    public class TriangleContainer
    {
        public struct Element
        {
            public ulong val;
            public int threadID;
        }

        private Element[,] table;
        private Semaphore globalSemaphore;
        private Semaphore writingSemaphore;
        private Semaphore checkingSemaphore;

        private int level;
        public int Level
        {
            get { return level; }
            set { level = value; }
        }

        public TriangleContainer(int level)
        {
            table = new Element[level, level];
            globalSemaphore = new Semaphore(1, 1, "GlobalSemaphore");
            writingSemaphore = new Semaphore(1, 1);
            checkingSemaphore = new Semaphore(1, 1);
            this.level = level;

            //Ustawienie wszystkich wartosci w tablicy na -1;
            for (int i = 0; i < level; i++)
            {
                for (int j = 0; j < level; j++)
                {
                    table[i, j] = new Element();
                    table[i,j].val = 0;
                    table[i,j].threadID = -1;;
                }
            }
        }

        public ulong GetValue(int i, int j)
        {
            writingSemaphore.WaitOne();
            ulong val = table[i, j].val;
            writingSemaphore.Release();
            return val;    
        }

        public int GetThreadID(int i, int j)
        {
            writingSemaphore.WaitOne();
            int id = table[i, j].threadID;
            writingSemaphore.Release();
            return id;
        }

        public void SetValue(int i, int j, ulong val, int ID)
        {
            writingSemaphore.WaitOne();
            table[i, j].val = val;
            table[i, j].threadID = ID;
            writingSemaphore.Release();
        }

        public bool CheckElement(int i, int j, int ID)
        {
            bool ret = false;

            checkingSemaphore.WaitOne();

            int tid = GetThreadID(i, j);

            
            if (tid == -1)
            {
                //table[i, j].val = 0;
                //table[i, j].threadID = ID;

                SetValue(i, j, 0, ID);
                ret = true;
            }
            checkingSemaphore.Release();

            return ret;
        }

        public void UncheckElement(int i, int j)
        {
            checkingSemaphore.WaitOne();
            //table[i, j].val = -1;
            //table[i, j].threadID = -1;
            SetValue(i, j, 0, -1);
            checkingSemaphore.Release();
        }

        /*public void FindFirstAndCheck(out int ii, out int jj)
        {
            ii = -1;
            jj = -1;
            bool founded = false;

            for (int i = 0; i < level; i++)
            {
                for (int j = 0; j <= i; j++)
                {
                    

                    Element e = table[i, j];

                    if (e.val == -1)
                    {
                        globalSemaphore.WaitOne();

                        e.val = 0;
                        ii = i;
                        jj = j;
                        founded = true;

                        globalSemaphore.Release();
                    }

                    

                    if (founded)
                        break;
                }

                if (founded)                
                    break;
                
            }
        }*/

        public bool IsSolved()
        {
            bool s = true ;

            for (int i = 0; i < level; i++)
            {
                if (table[level - 1, i].val <= 0)
                {
                    s = false;
                    break;
                }
            }

            return s;
        }

        public string LastLine()
        {
            string s = "";
            for (int i = 0; i < level; i++)
            {
                Element e = table[level - 1, i];
                s += "(" + e.val.ToString() + ";" + e.threadID.ToString() + ")";
                
            }

            return s;
        }
    }
}
